#include <malloc.h>
#include <string.h>

#include "task.h"

char * __class_keywords[] =
{
    "TEST",
    "TODO",
    "DONE",
    "RESEARCH",
    "QUESTION",
    "NEXT",
    "INFO",
    "REJECTED",
    NULL
};



char __tag_delim[] = ":";


struct task * new_task(struct task_header * header,
                       struct task_body   * body)
{
    struct task * task =
        (struct task *) malloc (sizeof(struct task));

    if (! task)
        return NULL;

    task->body = body;
    task->header = header;
    
    task->_child = NULL;
    task->_next = NULL;
    task->_parent = NULL;
    
    return task;
}


struct task_body * new_task_body(char * text)
{
    struct task_body * task_body =
        (struct task_body *) malloc (sizeof(struct task_body));

    if (! task_body)
        return NULL;

    task_body->text = text;
    task_body->times.count = 0;
    

    return task_body;
}

struct task_header * new_task_header()
{
    struct task_header * task_header =
        (struct task_header *) malloc (sizeof(struct task_header));

    if (! task_header)
        return NULL;

    memset(task_header, 0x0, sizeof(struct task_header));
    
    return task_header;
}


struct task_header * add_tag_to_header(struct task_header * dst, char const * tag)
{
    struct task_header * header = dst ? dst : new_task_header();

    char __buffer[65535];
    
    if (header->tags.count + 1>= MAX_TIMES)
    {
        return header;
    }
    

    if (tag)
    {
        int taglen = strlen(tag);
        
        memcpy(__buffer, tag, sizeof(__buffer) > taglen ? taglen : sizeof(__buffer));
        
        if (tag[0] == ':')
        {
            /* Tokenize tag line first*/
            char * context = NULL;
            char * token = strtok_r(__buffer, __tag_delim, &context);

            if (! token)
                return header;

            while (token)
            {
                header->tags.tags[header->tags.count ++] = strdup(token);

                printf("|TAG: %s added|", header->tags.tags[header->tags.count-1]);
                
                token = strtok_r(NULL, __tag_delim, &context);
                if (strcmp(token,"\n") == 0) token = NULL;
                
            }
                        
        }
        else
        {
            header->tags.tags[header->tags.count ++] = strdup(tag);
        }

    }
    
    return header;
}


struct task_header * add_level_to_header(struct task_header * dst, int level)
{
    struct task_header * header = dst ? dst : new_task_header();

    header->level = level;

    return header;
}


struct task_header * add_state_to_header(struct task_header * dst, const char * state)
{
    struct task_header * header = dst ? dst : new_task_header();

    header->state = strdup(state);

    return header;
}


struct task_header * add_caption_to_header(struct task_header * dst, const char * caption)
{
    struct task_header * header = dst ? dst : new_task_header();

    header->caption = strdup(caption);

    return header;
}

struct task_header * append_caption_to_header(struct task_header * dst, const char * caption)
{
    struct task_header * header = dst ? dst : new_task_header();

    header->caption = append_string(header->caption, caption);

    return header;
}


struct task_header * add_priority_to_header(struct task_header * dst, char priority)
{
    struct task_header * header = dst ? dst : new_task_header();

    header->priority = priority;

    return header;
}

struct task_header * add_completion_to_header(struct task_header * dst, int completion)
{
    struct task_header * header = dst ? dst : new_task_header();

    header->completion = completion;

    return header;
}

void free_task(struct task * task)
{
    free(task->header->caption);
    free(task->header->state);
    free(task->header);
    free(task->body->text);
    free(task->body);
    free(task);
}

void print_task(struct task * task)
{

    
    printf("Task, level %d:\n", task->header->level);
    printf("State:   %s\n", task->header->state);
    printf("Prio:    %c\n", task->header->priority);
    printf("Caption: %s\n", task->header->caption);
    printf("Complete:[%d/%d]\n",
           task->header->completion >> sizeof(int) * 4,
           task->header->completion & 0xffff);

    if (task->header->tags.count)
    {
        int j;
        
        for (j=0; j<task->header->tags.count; j++)
        {
            printf("%s; ",
                   task->header->tags.tags[j]);
        }
        printf("\n");
        
    }
    
    printf("Text:\n%s\n",  task->body->text);
    
    if (task->body->times.count)
    {
        int i;
        
        printf("Timers:\n");
        
        
        for (i=0; i<task->body->times.count; i++)
        {
            printf("%d, type %d\n",
                   task->body->times.times[i].timestamp,
                   task->body->times.times[i].type);
        }
    }

    
    
    printf("---- END OF TASK ----\n");
}


struct task_body * add_date_to_body(struct task_body * body, time_t time, char type)
{
    int i = body->times.count ++;
    if (i < MAX_TIMES)
    {
        body->times.times[i].timestamp = time;
        body->times.times[i].type = type;
    }

    return body;
}


struct task_body * add_text_to_body(struct task_body * body, const char * text)
{
    body->text = append_string(body->text, text);

    return body;
}


struct task_list * new_task_list()
{
    struct task_list * task_list =
        (struct task_list * ) malloc (sizeof(struct task_list));

    if (! task_list)
        return NULL;


    task_list->first = NULL;
    task_list->first = NULL;
}

struct task_list * add_task_to_list(struct task_list * task_list,
                                    struct task * task)
{
    /* If level is equivalent, then add task to _next of latest item
     * If level is bigger, then add task to _child of latest item
     * If level is less, then.. then search latest of latest of latest
          with same level. From bottom to top. :]  */

    struct task_list * list = task_list
        ? task_list
        : new_task_list();

    struct task * i;
    
    if (! list)
        return NULL;

    if (list->last == NULL)
    {
        list->first = task;
        list->last  = task;
        task->_parent = NULL;
        printf("|NULL->(%s)|",
               task->header->caption);
        
        return list;
    }
    

    if (list->last->header->level
        == task->header->level)
    {
        task->_parent = list->last->_parent;
        list->last->_next = task;
        list->last = task;
        
        printf("|%s->(%s)|",
               task->_parent ? task->_parent->header->caption : "NULL",
               task->header->caption);
        
        return list;
    }
    
    if (list->last->header->level
        <  task->header->level)
    {
        task->_parent = list->last;
        list->last->_child = task;
        list->last = task;

        printf("|%s=c>(%s)|",
               task->_parent ? task->_parent->header->caption : "NULL",
               task->header->caption);

        return list;
    }

    /* Ok. Search to latest parent with same level, or NULL. */
    for (i=list->last->_parent; (i != NULL )
             && (task->header->level
                 != i->header->level);
         i = i->_parent);

    if (i)
    {
        i->_next = task;
        list->last = task;
        task->_parent = i->_parent;
        printf("|%s->(%s)|",
               task->_parent ? task->_parent->header->caption : "NULL",
               task->header->caption);

    }
    else
    {
        list->first->_next = task;
        task->_parent = NULL;
        list->last = task;
        printf("|2NULL->(%s)|",
               task->header->caption);

    }
    
    return task_list;
}


void __print_tasks_subtree(struct task * task)
{
    struct task * i;

    if (! task)
        return;
        
    for (i=task; i != NULL; i=i->_next)
    {
        print_task(i);
        __print_tasks_subtree(i->_child);
    }
}


/* Recursively print subtree */
void print_tasks(struct task_list * tasks)
{
    __print_tasks_subtree(tasks->first);
}

        
